const sourceMap = require('source-map');
const fs = require("fs");
const path = require("path");
const Koa = require('koa');
const cors = require('@koa/cors');
const koaStatic2 = require('koa-static2');
const Router = require('koa2-router');
const router = new Router();

const app = new Koa();

// config
var config = {
    port: 8088
};
//正常情况，此处错误来自数据库
var errlist = [
    //这是具有stack形式的报错记录，react的错误边界就会有这种情况
    /* {
        id:"1",
        msg: "Error: Minified React error #152; visit https://reactjs.org/docs/error-decoder.html?invariant=152&args[]=component for the full message or use the non-minified dev environment for full errors and additional helpful warnings.",
        type:"reactRecord",
        stackArr:[
            {
                "url": "http://localhost:30053/home/main.bundle.js",
                "row": "1",
                "col": "41507"
            },
        ]
    } */
];
var sourcesPathMap = {};
var id = 0;

function getItemFromList(list, obj) {
    var key = Object.keys(obj)[0];
    var val = obj[key];

    var res = null;
    list.map(item => {
        if (item[key] == val) {
            res = item;
        }
    });
    return res;
}

//注册允许跨域
app.use(cors());


var viewPath = path.join(__dirname, 'view');
//source-map的文件夹路径
var smDir = "D:\\project\\test\\recordError\\view\\createError"

//路由
app.use(router)
//静态文件
app.use(koaStatic2("", viewPath));


function fixPath(filepath) {
    return filepath.replace(/\.[\.\/]+/g, "");
}
// 查找sourcemap
function lookupSourceMap(mapFile, line, column, callback) {
    fs.readFile(mapFile, async function(err, data) {
        if(err) {
            console.error(err);
            return;
        }

        var fileContent = data.toString(),
            fileObj = JSON.parse(fileContent),
            sources = fileObj.sources;

        sources.map(item => {
            sourcesPathMap[fixPath(item)] = item;
        });

        var consumer = await new sourceMap.SourceMapConsumer(fileContent);
        var lookup = {
            line: parseInt(line),
            column: parseInt(column)
        };
        var result = consumer.originalPositionFor(lookup);

        var originSource = sourcesPathMap[result.source],
            sourcesContent = fileObj.sourcesContent[sources.indexOf(originSource)];

        result.sourcesContent = sourcesContent;

        callback && callback(result);
    });
}
//获取sourceMap信息
function* getSmInfo(backArr){

    for (let i=0;i<backArr.length;i++)
    { 
        var url = backArr[i].url,
            row = backArr[i].row,
            col = backArr[i].col;
        var filename = path.basename(url);
        let bc = new Promise(function(resolve, reject) {
            lookupSourceMap(path.join(smDir, (filename + ".map")), row, col, function(res){
                // var source = res.source;
                // var filename = path.basename(source);
                // var filepath = path.join(smDir, filename);
                resolve({
                    file: res.sourcesContent,
                    source: res.source,
                    row: res.line,
                    column: res.column,
                });
                
            });
        });
        yield bc
    }
    return;
}


router.all('/errlist', (ctx, next) => {
    ctx.body = {
        errlist: errlist,
    };
});

//记录报错
router.all('/report', (ctx, next) => {
    var paramObj = ctx.query;

    var msg = paramObj.msg,
    url = paramObj.url,
    row = paramObj.row,
    col = paramObj.col;

    if (url) {
        paramObj.id = ++id;
        errlist.push(paramObj);
    }
});


router.all('/errdetail', async (ctx, next) => {
    var errid = ctx.query.errid;
    var obj = getItemFromList(errlist, {id: errid});
    var type = obj?.type;
    //react自己记录的
    if(type == "reactRecord"){
        let stackArr = obj.stackArr;
        let backArr = []
        for await(let ele of getSmInfo(stackArr)) {
            backArr.push(ele)
        }
        ctx.body = backArr;
    }else{
        var url = obj.url,
            row = obj.row,
            col = obj.col;
        var filename = path.basename(url);
        var detailInfo = await new Promise(function(resolve, reject) {
            lookupSourceMap(path.join(smDir, (filename + ".map")), row, col, function(res){
                resolve({
                    file: res.sourcesContent,
                    msg: obj.msg,
                    source: res.source,
                    row: res.line,
                    column: res.column,
                });
            });
        })
        ctx.body = detailInfo;
    }
});

//路由重定向到首页
router.get('/', (ctx, next) => {
    ctx.response.redirect("/index.html")
});

app.listen(config.port, function() {
    console.log('服务启动成功: listening on port ' + config.port);
});
  